#encoding=utf8
'''
Created on Oct 28, 2014

@author: jingyang <jingyang@smalltreemedia.com>
'''
from functools import partial

from django.contrib import admin
from django.contrib.admin import widgets
from django.contrib.admin.options import get_ul_class
from django.forms.models import modelformset_factory
from django.utils import timezone

import settings
from core.widgets import CustomForeignKeyRawIdWidget
from app_info.models import UserGroup
from ads_info.models import Network, UnitType, AdsUnit, AdsInfo,\
    CROSS_PROMO_NETWORK_NAME, CrossPromoTokenName, CrossPromoVendor,\
    CrossPromoReq, CrossPromoToken
from ads_info.forms import AdsInfoForm, CrossPromoReqForm, CrossPromoTokenForm
from utils import field, DateListFilter
from threads import UpdateGoogleSheetThread
from core.tasks import update_google_sheet

from django.views.decorators.csrf import csrf_protect
from django.utils.decorators import method_decorator
from django.db import transaction
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse


csrf_protect_m = method_decorator(csrf_protect)


class UnitTypeInline(admin.TabularInline):
    model = UnitType


class NetworkAdmin(admin.ModelAdmin):
    list_display = ('name', 'status', 'get_unit_type')
    inlines = [UnitTypeInline]
    list_per_page = 20

    @field(u'Unit', allow_tags=True)
    def get_unit_type(self, obj):
        return "<br />".join([u.__unicode__() for u in obj.unittype_set.all()])

    def get_actions(self, request):
        """
        remove delete action
        """
        actions = super(NetworkAdmin, self).get_actions(request)
        if actions.get('delete_selected'):
            del actions['delete_selected']
        return actions

    @csrf_protect_m
    @transaction.commit_on_success
    def change_view(self, request, object_id, form_url='', extra_context=None):
        user = request.user
        if user.groups.filter(name='Management').exists() and not user.is_superuser:
            raise PermissionDenied
        return admin.ModelAdmin.change_view(self, request, object_id, form_url=form_url, extra_context=extra_context)


class AdsUnitInline(admin.TabularInline):
    model = AdsUnit
    can_delete = False
    extra = 3
    readonly_fields = ('unit', 'editor', 'timestamp')
    exclude = ('latest_get_time', 'fail_count', 'first_get_time',
        'activation')
    fieldsets = ((None, {'fields': ('unit', 'value', 'status', 'editor', 'timestamp')}),)

    def get_formset(self, request, obj=None, **kwargs):
        if obj:
            self.extra = 0
            self.max_num = obj.adsunit_set.count()
        return admin.TabularInline.get_formset(self, request, obj=obj, **kwargs)

    def get_readonly_fields(self, request, obj=None):
        if obj:
            if not request.user.is_superuser and\
            not request.user.groups.filter(name__in=['Post-production Staff', 'Ad Ops Team']).exists():
                return ('unit', 'value', 'status', 'activation')
        return admin.TabularInline.get_readonly_fields(self, request, obj=obj)


class CreatedMonthListFilter(DateListFilter):
    title = 'Created at Month'
    parameter_name = 'month'
    filter_field = 'create_time'


class AdsInfoAdmin(admin.ModelAdmin):
    list_display = ('request_id', 'get_editor_name', 'create_time', 'status', 'app',
        'get_units', 'note', ) # 'get_cross_promo', 'get_cross_promo_ads'
                               # heyulin 2016-3-22 , because need hide "Require Cross Promo" and "Cross Promo Ads"
    list_editable = ('status',)
    exclude = ('editor', 'request_id', 'status', 'cross_promo')
    raw_id_fields = ('app',)
    search_fields = ('request_id', 'app__prj_code', 'app__app_name')
    list_filter = ('status', 'cross_promo', 'app__platform', 'app__app_store',
        CreatedMonthListFilter)
    list_per_page = 20
#    inlines = [AdsUnitInline]
    form = AdsInfoForm
    fieldsets = (
        (None, {
            'fields':
            (
            'app',
            'orientation',
            'units',
#             'cross_promo',
            'cross_promo_ads',
            'cross_local',
            'cross_remote',
            'note',
            )
        }),
    )

    class Media:
        js = ('base/js/custom_ads_admin.js', 'base/js/RelatedObjectLookups.js')

    def save_formset(self, request, form, formset, change):
        """
        Given an inline formset save it to the database.
        """
        formset.save()
        if formset.model == AdsUnit:
            changed_units = formset.save(commit=False)
            for u in changed_units:
                if not u.status:
                    if u.value:
                        u.status = 'A'
                u.editor = request.user.email
                u.timestamp = timezone.localtime(timezone.now())
                u.save()
            obj = form.save(commit=False)
            statuses = obj.adsunit_set.values_list('status', flat=True)
            if list(set(statuses)) == ['A']:
                obj.status = 'F'
                obj.save()

    def get_list_display(self, request):
        if not request.user.is_superuser and\
        not request.user.groups.filter(name__in=['Post-production Staff', 'Ad Ops Team']).exists():
            return ['get_status' if x == 'status' else x for x in self.list_display]
        return admin.ModelAdmin.get_list_display(self, request)

    def get_inline_instances(self, request, obj=None):
        if obj:
            self.inlines = [AdsUnitInline]
        else:
            self.inlines = []
        return admin.ModelAdmin.get_inline_instances(self, request, obj=obj)

    def get_fieldsets(self, request, obj=None):
        if obj:
            return ((None, {'fields':
            (
            'get_app',
            'get_platform',
            'get_app_store',
            'get_studio',
            'get_app_id',
            'get_full_bundle_id',
            'orientation',
#             'cross_promo',
            'get_cross_promo_ads',
            'cross_local',
            'cross_remote',
            'note',
            )
        }),)
        if self.declared_fieldsets:
            return self.declared_fieldsets
        form = self.get_form(request, obj)
        fields = list(form.base_fields) + list(self.get_readonly_fields(request, obj))
        return [(None, {'fields': fields})]

    def get_readonly_fields(self, request, obj=None):
        if obj:
            return ('app', 'get_app', 'orientation', 'cross_promo', 'cross_promo_ads',
                'get_cross_promo_ads', 'cross_local', 'cross_remote', 'note',
                'get_platform', 'get_app_store', 'get_studio', 'get_app_id',
                'get_full_bundle_id')
        return self.readonly_fields

    @field('Platform', admin_order_field='app__platform')
    def get_platform(self, obj):
        return obj.app.get_platform_display()

    @field('App Store', admin_order_field='app__app_store')
    def get_app_store(self, obj):
        return obj.app.app_store

    @field('Studio', admin_order_field='app__studio')
    def get_studio(self, obj):
        return obj.app.studio

    @field('Apple ID', admin_order_field='app__app_id')
    def get_app_id(self, obj):
        return obj.app.app_id or ''

    @field('Full Bundle ID', admin_order_field='app__bundle_id_suffix')
    def get_full_bundle_id(self, obj):
        return obj.app.get_full_bundle_id()

    @field('Request Status', allow_tags=True, admin_order_field='status')
    def get_status(self, obj):
        if obj.status == 'N':
            cls = 'label-warning'
        elif obj.status == 'F':
            cls = 'label-success'
        elif obj.status == 'I':
            cls = 'label-important'
        elif obj.status == 'C':
            cls = ''
        else:
            cls = 'label-info'
        return "<span class='label %s' style='width: 40px;'><center>%s</center></span>" %\
            (cls, obj.get_status_display())

    @field("Require Cross Promo?", boolean=True, admin_order_field='cross_promo')
    def get_cross_promo(self, obj):
        return obj.cross_promo == 'Y'

    @field("Editor", admin_order_field='editor')
    def get_editor_name(self, obj):
        return obj.editor.split("@")[0]

    @field('App', allow_tags=True, admin_order_field='app__prj_code')
    def get_app(self, obj):
        return "<a href='%s%s/' target='_blank'>%s</a>" % ('/admin/app_info/app/',
            obj.app_id, obj.app)

    @field('Cross Promo Ads', allow_tags=True)
    def get_cross_promo_ads(self, obj, sp="<br />"):
        if obj.cross_promo_ads and not obj.cross_promo_ads == '[]':
            data = str(obj.cross_promo_ads).replace('[', '').replace(']', '').\
                replace("u'", '').replace("'", '').split(', ')
            return sp.join([u.__unicode__() for u in UnitType.objects.filter(id__in=data)])
        return ""

    @field(u'Ads Requested', allow_tags=True)
    def get_units(self, obj):
        return "<br />".join([u.unit.__unicode__() for u in obj.adsunit_set.\
            exclude(unit__network__name=CROSS_PROMO_NETWORK_NAME)])

    def get_actions(self, request):
        """
        remove delete action
        """
        actions = super(AdsInfoAdmin, self).get_actions(request)
        if actions.get('delete_selected'):
            del actions['delete_selected']
        return actions

    def get_changelist_formset(self, request, **kwargs):
        """
        Returns a FormSet class for use on the changelist page if list_editable
        is used.
        """
        if not request.user.is_superuser and\
        not request.user.groups.filter(name__in=['Post-production Staff', 'Ad Ops Team']).exists():
            list_editable = ()
        else:
            list_editable = self.list_editable

        defaults = {
            "formfield_callback": partial(self.formfield_for_dbfield, request=request),
        }
        defaults.update(kwargs)
        return modelformset_factory(self.model,
            self.get_changelist_form(request), extra=0,
            fields=list_editable, **defaults)

    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
        db = kwargs.get('using')
        if db_field.name in self.raw_id_fields:
            kwargs['widget'] = CustomForeignKeyRawIdWidget(db_field.rel,
                                    self.admin_site, using=db)
        elif db_field.name in self.radio_fields:
            kwargs['widget'] = widgets.AdminRadioSelect(attrs={
                'class': get_ul_class(self.radio_fields[db_field.name]),
            })
            kwargs['empty_label'] = db_field.blank and _('None') or None

        return db_field.formfield(**kwargs)

    def queryset(self, request):
        qs = self.model._default_manager.get_query_set()
#        user_group = UserGroup.objects.filter(owner=request.user)
#        if request.user.is_superuser:
#            qs = self.model._default_manager.get_query_set()
#        else:
#            if user_group.exists():
##                qs = self.model._default_manager.get_query_set()
#                res = []
#                for g in user_group:
#                    res += [user.email for user in g.user.all()]
#                res += [request.user.email]
#                qs = AdsInfo.objects.filter(editor__in=list(set(res)))
#            else:
#                qs = AdsInfo.objects.filter(editor=request.user.email)
        # TODO: this should be handled by some parameter to the ChangeList.
        ordering = self.get_ordering(request)
        if ordering:
            qs = qs.order_by(*ordering)
        return qs

#    def response_add(self, request, obj, post_url_continue=None):
#        if not settings.DEBUG:
#            data_list = [obj.request_id, obj.get_local_create_time(), obj.editor,
#                obj.app.prj_code, obj.app.app_name, obj.app.get_platform_display(),
#                "%s\n%s" % (obj.app.app_id or "", obj.app.get_full_bundle_id()),
#                obj.get_orientation_display(),
#                "\n".join([u.unit.__unicode__() for u in obj.get_units()]),
#                obj.note, self.get_cross_promo_ads(obj, sp="\n") if obj.cross_promo == 'Y' else "No Cross Promo",
#                obj.cross_remote]
#            UpdateGoogleSheetThread(settings.ADS_REQUEST_FILE, data_list).start()
##            update_google_sheet.delay(settings.ADS_REQUEST_FILE, data_list)
#        return super(AdsInfoAdmin, self).response_add(request, obj,
#            post_url_continue=post_url_continue)

    def save_model(self, request, obj, form, change):
        if not change:
            obj.editor = request.user.email
            current_year = timezone.now().year
            current_month = timezone.now().month
            current_day = timezone.now().day
            latest = AdsInfo.objects.filter(create_time__year=current_year,
                create_time__month=current_month, create_time__day=current_day).order_by('-request_id')[:1]
            if latest:
                latest = latest[0]
                num = int(latest.request_id[-2:]) + 1
            else:
                num = 1
            obj.request_id = 'RADS-%s%s%s%s' % (current_year, '%02i' % current_month,
                '%02i' % current_day, '%02i' % num)
            obj.save()
            for u in obj.units:
                AdsUnit.objects.create(info=obj, unit=u)
            cross_promo_ads = form.cleaned_data.get('cross_promo_ads')
            for c in cross_promo_ads:
                AdsUnit.objects.create(info=obj, unit=c)
            obj.cross_promo_ads = str([str(c.pk) for c in cross_promo_ads])
        obj.save()


class CrossPromoTokenNameInline(admin.TabularInline):
    model = CrossPromoTokenName
    extra = 1


class CrossPromoVendorAdmin(admin.ModelAdmin):
    list_display = ('name', 'status', 'get_token_names')
    list_filter = ('status',)
    inlines = [CrossPromoTokenNameInline]

    @field('Tokens', allow_tags=True)
    def get_token_names(self, obj):
        return '<br />'.join([unicode(t) for t in obj.crosspromotokenname_set.all()])

    def get_actions(self, request):
        """
        remove delete action
        """
        actions = super(CrossPromoVendorAdmin, self).get_actions(request)
        if actions.get('delete_selected'):
            del actions['delete_selected']
        return actions


class CrossPromoTokenInline(admin.TabularInline):
    model = CrossPromoToken
    extra = 0
    can_delete = False
    form = CrossPromoTokenForm
    readonly_fields = ('token', 'editor', 'timestamp')
    fieldsets = ((None, {'fields': ('token', 'value', 'status', 'editor',
                                    'timestamp')}),)

    def get_formset(self, request, obj=None, **kwargs):
        if obj:
            self.max_num = obj.crosspromotoken_set.count()
        return admin.TabularInline.get_formset(self, request, obj=obj, **kwargs)

    def get_readonly_fields(self, request, obj=None):
        if obj:
            if not request.user.is_superuser and not request.user.groups.filter(name__in=['Post-production Staff']).exists():
                return ('token', 'value', 'status', 'editor', 'timestamp')
        return admin.TabularInline.get_readonly_fields(self, request, obj=obj)


class CrossPromoReqAdmin(admin.ModelAdmin):
    list_display = ('request_id', 'editor', 'create_time', 'status', 'get_app',
                    'get_tokens')
    exclude = ('editor', 'request_id', 'status')
    list_editable = ('status',)
    search_fields = ('request_id', 'app__prj_code', 'editor')
    list_filter = ('orientation', 'status')
    raw_id_fields = ('app',)
    form = CrossPromoReqForm
    add_fieldsets = (None, {'fields': (
                                'app',
                                'orientation',
                                'token',
                                'cross_local',
                                'cross_remote',
                                'notes',
                                )
                            }
                     )
    view_fieldsets = (None, {'fields': (
                                'get_app',
                                'get_platform',
                                'get_store',
                                'get_studio',
                                'get_apple_id',
                                'get_full_bundle_id',
                                'orientation',
                                'get_tokens',
                                'cross_local',
                                'cross_remote',
                                'notes',
                                'status',
                                )
                             }
                      )

    class Media:
        js = ('base/js/custom_cross_promo_admin.js',
              'base/js/RelatedObjectLookups.js')

    @field('App', allow_tags=True, admin_order_field='app__prj_code')
    def get_app(self, obj):
        return "<a target='_blank' href={}>{}</a>"\
            .format(reverse('admin:app_info_app_change', args=[obj.app_id]),
                    obj.app)

    @field('Platform')
    def get_platform(self, obj):
        return obj.app.get_platform_display()

    @field('App Store')
    def get_store(self, obj):
        return obj.app.app_store

    @field('Studio')
    def get_studio(self, obj):
        return obj.app.studio

    @field('Apple ID')
    def get_apple_id(self, obj):
        return obj.app.app_id

    @field('Full Bundle ID')
    def get_full_bundle_id(self, obj):
        return obj.app.get_full_bundle_id()

    @field('Tokens', allow_tags=True)
    def get_tokens(self, obj):
        ls = obj.crosspromotoken_set.values_list('token__vendor__name',
                                                 'token__name')
        return "<br />".join(['%s - %s' % l for l in ls])

    def get_inline_instances(self, request, obj=None):
        if obj:
            self.inlines = [CrossPromoTokenInline]
        else:
            self.inlines = []
        return admin.ModelAdmin.get_inline_instances(self, request, obj=obj)

    def get_readonly_fields(self, request, obj=None):
        if obj:
            return self.view_fieldsets[1]['fields'] + ('app', 'token')
        return self.readonly_fields

    def get_fieldsets(self, request, obj=None):
        if not obj:
            return (self.add_fieldsets,)
        return (self.view_fieldsets,)

    def get_actions(self, request):
        """
        remove delete action
        """
        actions = super(CrossPromoReqAdmin, self).get_actions(request)
        if actions.get('delete_selected'):
            del actions['delete_selected']
        return actions

    def get_changelist_formset(self, request, **kwargs):
        """
        Returns a FormSet class for use on the changelist page if list_editable
        is used.
        """
        if not request.user.is_superuser and not request.user.groups.filter(name__in=['Post-production Staff']).exists():
            list_editable = ()
        else:
            list_editable = self.list_editable

        defaults = {
            "formfield_callback": partial(self.formfield_for_dbfield, request=request),
        }
        defaults.update(kwargs)
        return modelformset_factory(self.model,
                                    self.get_changelist_form(request),
                                    extra=0,
                                    fields=list_editable, **defaults)

    def save_formset(self, request, form, formset, change):
        """
        Given an inline formset save it to the database.
        """
        formset.save()
        if formset.model == CrossPromoToken:
            changed_tokens = formset.save(commit=False)
            for u in changed_tokens:
                if not u.status:
                    if u.value:
                        u.status = 'A'
                u.editor = request.user.email
                u.timestamp = timezone.localtime(timezone.now())
                u.save()
            obj = form.save(commit=False)
            statuses = obj.crosspromotoken_set.values_list('status', flat=True)
            if list(set(statuses)) == ['A']:
                obj.status = 'R'
                obj.save()

    def save_model(self, request, obj, form, change):
        if not change:
            obj.editor = request.user.email
            now = timezone.now()
            num = CrossPromoReq.objects.get_latest_num()
            obj.request_id = 'RCPS-{date:%Y%m%d}{num:02X}'.format(date=now,
                                                                  num=num)
            obj.save()

            tokens = form.cleaned_data.get('token')
            for token in tokens:
                CrossPromoToken.objects.create(req=obj, token=token)
        obj.save()


admin.site.register(Network, NetworkAdmin)
admin.site.register(AdsInfo, AdsInfoAdmin)
admin.site.register(CrossPromoVendor, CrossPromoVendorAdmin)
admin.site.register(CrossPromoReq, CrossPromoReqAdmin)
